package java8.deep;

import cn.hutool.core.util.IdUtil;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import org.junit.Test;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.function.BinaryOperator;
import java.util.stream.Collectors;

/**
 * @program JavaBase
 * @description: 参考 https://www.matools.com/api/java8
 * @author: zhanglu
 * @create: 2021-06-18 09:28:00
 */
public class CollectorsTest {

    private static List<StatisticalData> dataList = new ArrayList<>();

    static {
        for (int i = 0; i < 50; i++) {
            dataList.add(StatisticalData.builder()
                    .id((long) i)
                    .count(i)
                    .code("TEST")
                    .description("测试一下")
                    .d((double) (48/(i+1)))
                    .f((float) (48/(i+1)))
                    .b(new BigDecimal(i + 1).multiply(BigDecimal.valueOf(8)))
                    .build());
        }
    }

    @Test
    public void test01(){
        //返回一个 Collector ，它产生应用于输入元素的双值函数的算术平均值。
        Double d = dataList.stream().collect(Collectors.averagingDouble(StatisticalData::getD));
        System.out.println(d);
        d = dataList.stream().collect(Collectors.averagingDouble(StatisticalData::getF));
        System.out.println(d);
        //返回一个 Collector ，它产生应用于输入元素的整数值函数的算术平均值。
        d = dataList.stream().collect(Collectors.averagingInt(StatisticalData::getCount));
        System.out.println(d);
        //返回一个 Collector ，它产生应用于输入元素的长值函数的算术平均值。
        d = dataList.stream().collect(Collectors.averagingLong(StatisticalData::getId));
        System.out.println(d);
    }

    @Test
    public void test02(){
        //适应 Collector进行额外的整理转换。
        Long count = dataList.stream().map(StatisticalData::getId).collect(Collectors.toList())
                .stream().collect(Collectors.counting());
        System.out.println(count);
        count = dataList.stream()
                .map(StatisticalData::getId)
                .collect(Collectors.collectingAndThen(Collectors.toList(), x -> {
                    return x.stream().collect(Collectors.counting());
                }));
        System.out.println(count);
    }

    @Test
    public void test03(){
        List<Map.Entry<String, List<StatisticalData>>> collect = dataList.stream()
                .collect(Collectors.groupingBy(o -> o.getCode())).entrySet().stream().collect(Collectors.toList());
        System.out.println(collect);
        //返回 Collector “由基团”上的类型的输入元件操作实现 T ，根据分类功能分组元素，并且在返回的结果 Map 。
        Map<String, List<StatisticalData>> m1 = dataList.stream()
                .collect(Collectors.groupingBy(StatisticalData::getCode));
        m1 = dataList.stream()
                .collect(Collectors.groupingBy(StatisticalData::getCode, Collectors.toList()));
        //返回 Collector “由基团”上的类型的输入元件操作实现级联 T ，根据分类功能分组元素，然后使用下游的指定执行与给定键相关联的值的归约运算 Collector 。
        Map<String, Long> m2 = dataList.stream()
                .collect(Collectors.groupingBy(StatisticalData::getCode, Collectors.counting()));
        System.out.println(m2);
        Map<String, List<Long>> m3 = dataList.stream()
                .collect(Collectors.groupingBy(StatisticalData::getCode, Collectors.mapping(StatisticalData::getId, Collectors.toList())));
        //返回 Collector “由基团”上的类型的输入元件操作实现级联 T ，根据分类功能分组元素，然后使用下游的指定执行与给定键相关联的值的归约运算 Collector 。
        Map<String, Long> m4 = dataList.stream()
                .collect(Collectors.groupingBy(StatisticalData::getCode,
                        () -> new HashMap<>(), Collectors.counting()));
        System.out.println(m4);
        //返回一个并发 Collector “由基团”上的类型的输入元件操作实现 T ，根据分类功能分组元素。
        ConcurrentMap<String, List<StatisticalData>> cm1 = dataList.stream()
                .collect(Collectors.groupingByConcurrent(StatisticalData::getCode));
        //返回一个并发 Collector “由基团”上的类型的输入元件操作实现级联 T ，根据分类功能分组元素，然后使用下游的指定执行与给定键相关联的值的归约运算 Collector 。
        cm1 = dataList.stream()
                .collect(Collectors.groupingByConcurrent(StatisticalData::getCode, Collectors.toList()));
        //返回一个并发 Collector “由基团”上的类型的输入元件操作实现级联 T ，根据分类功能分组元素，然后使用下游的指定执行与给定键相关联的值的归约运算 Collector 。
        ConcurrentSkipListMap<String, Long> cm2 = dataList.stream()
                .collect(Collectors.groupingByConcurrent(StatisticalData::getCode,
                        () -> new ConcurrentSkipListMap<>(), Collectors.counting()));
    }

    @Test
    public void test04(){
        String res = dataList.stream().map(StatisticalData::getCode).collect(Collectors.joining());
        System.out.println(res);
        res = dataList.stream().map(StatisticalData::getCode).collect(Collectors.joining("-"));
        System.out.println(res);
        res = dataList.stream().map(StatisticalData::getCode).collect(Collectors.joining("-", "^", "$"));
        System.out.println(res);
    }

    @Test
    public void test05(){
        //适应一个 Collector类型的接受元件 U至类型的一个接受元件 T通过积累前应用映射函数到每个输入元素。
        Long res = dataList.stream().collect(Collectors.mapping(StatisticalData::getId, Collectors.counting()));
        System.out.println(res);
    }

    @Test
    public void test06(){
        Optional<StatisticalData> collect = dataList.stream().collect(Collectors.maxBy((o1, o2) -> o1.count - o2.count));
        System.out.println(collect.get());
        collect = dataList.stream().collect(Collectors.minBy((o1, o2) -> o1.count - o2.count));
        System.out.println(collect.get());
    }

    @Test
    public void test07(){
        //返回一个Collector ，它根据Predicate对输入元素进行Predicate ，并将它们组织成Map<Boolean, List<T>> 。 Map返回的类型，可变性，可串行性或线程安全性没有Map 。
        Map<Boolean, List<StatisticalData>> collect = dataList.stream().collect(Collectors.partitioningBy(o -> o.getCount() < 30));
        System.out.println(collect);
        //返回一个Collector ，它根据Predicate对输入元素进行Predicate ，根据另一个Collector减少每个分区中的值，并将其组织成Map<Boolean, D> ，其值是下游缩减的结果。
        //Map返回的类型，可变性，可串行性或线程安全性没有Map 。
        collect = dataList.stream().collect(Collectors.partitioningBy(o -> o.getCount() < 30, Collectors.toList()));
        System.out.println(collect);
        Map<Boolean, Long> c1 = dataList.stream().collect(Collectors.partitioningBy(o -> o.getCount() < 30, Collectors.counting()));
        System.out.println(c1);
    }

    /**
     * TODO ???
     */
    @Test
    public void test08(){
        //返回一个Collector ，它在指定的Collector下执行其输入元素的BinaryOperator 。 结果被描述为Optional<T> 。
        Map<Boolean, Optional<StatisticalData>> collect = dataList.stream()
                .collect(Collectors.partitioningBy(o -> o.getCount() < 30,
                Collectors.reducing(BinaryOperator.maxBy((o1, o2) -> o1.count - o2.count))));
        System.out.println(collect);

        //返回 Collector执行下一个指定的减少其输入元件的 BinaryOperator使用所提供的身份。
        Map<String, StatisticalData> c1 = dataList.stream()
                .collect(Collectors.groupingBy(StatisticalData::getCode,
                        Collectors.reducing(new StatisticalData(),
                                o -> StatisticalData.builder().id(o.id).count(o.count).build(),
                                BinaryOperator.maxBy((o1, o2) -> o1.count - o2.count))));
        System.out.println(c1);
    }

    @Test
    public void test09(){
        //返回一个 Collector，double生产映射函数应用于每个输入元素，并返回结果值的汇总统计信息。
        int s1 = dataList.stream().collect(Collectors.summarizingInt(o -> o.getCount())).getMin();
        System.out.println(s1);
        long s2 = dataList.stream().collect(Collectors.summarizingLong(o -> o.getCount())).getSum();
        System.out.println(s2);
        double s3 = dataList.stream().collect(Collectors.summarizingDouble(o -> o.getD())).getSum();
        System.out.println(s3);
        Integer s4 = dataList.stream().collect(Collectors.summingInt(o -> o.getCount()));
        System.out.println(s4);
        Long s5 = dataList.stream().collect(Collectors.summingLong(o -> o.getCount()));
        System.out.println(s5);
        Double s6 = dataList.stream().collect(Collectors.summingDouble(o -> o.getCount()));
        System.out.println(s6);
    }

    @Data
    @Accessors(chain = true)
    @Builder
    @AllArgsConstructor
    @NoArgsConstructor
    private static class StatisticalData{
        private Long id;
        private Integer count;
        private String code;
        private String description;
        private Double d;
        private Float f;
        private BigDecimal b;
    }

}
